#include <stdio.h>
#include <omp.h>

#include <vector>

#include "mpi.h"
#include "oblivious.h"
#include "distributed_mult.h"
#include "stdlib.h"


using namespace std;


struct MatrixReader {
  int rows;
  int cols;
  vector<double> m;
};

static void read_csv(const char *filename, MatrixReader &result) {

  FILE *file = fopen(filename, "r");
  if (!file) {
    perror("unable to open file: ");
    exit(1);
  }

  result.rows = result.cols = 0;

  while (!feof(file)) {    
    int c = fgetc(file);
    if (c == -1) {
      return;
    } else if (c == '\n') {
      result.rows++;
    } else if (isspace(c)) {
      continue;
    } else if (isdigit(c) || c == '-') {
      double d;
      ungetc(c, file);
      if (fscanf(file, "%lf", &d) == 1) {
        result.m.push_back(d);
        if (result.rows == 0)
          result.cols++;
      }
    }
  }

}

int main(int argc, char *argv[]) {

  int base_case_size = 100;

  if (argc < 3) {
    fprintf(stderr, "Wrong number of arguments (%d)\n", argc);
    fprintf(stderr, "usage: main a.csv b.csv\n");
    return 1;
  }

  MPI_Init(&argc, &argv);

  int my_rank, num_procs;
  MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
  MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

  MatrixReader a;
  MatrixReader b;
  double *my_c;

  read_csv(argv[1], a);
  read_csv(argv[2], b);
  my_c = (double*) malloc(sizeof(double) * a.rows * b.cols);

  if (my_rank == 0)
    printf("%s: (%d, %d); %s: (%d, %d)\n", argv[1], a.rows, a.cols, argv[2], b.rows, b.cols);
  
  //printf("Allocated memory\n");

  for (int i = 0; i < a.rows * b.cols; i++)
    my_c[i] = 0.0;

  //printf("Finished writing memory\n");

  // start timing

  int my_c_start = 0;
  for (int i = 0; i < my_rank; i++) {
    my_c_start += get_row_count(a.rows, i, num_procs);
  }

  //printf("Starting multiplication\n");

  double start_time = MPI_Wtime();
  distributed_mat_mult_rec(&a.m[0], &b.m[0], my_c, a.rows, a.cols, b.cols, my_c_start, 
			   base_case_size);
  double end_time = MPI_Wtime();
  
  // end timing

  if (my_rank == 0) {
    Matrix c_mat(my_c, a.rows, b.cols);
    c_mat.dump();
  }

  MPI_Finalize();
    
  return 0;

}
